/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package io.karim.materialtabs.sample.widget;

import com.ryan.ohos.extension.event.interfaces.ViewGroup;
import com.ryan.ohos.extension.widget.toolbar.Toolbar;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.List;

/**
 * ActionBar
 *
 * @since 2021/07/23
 */
public class ActionBar extends ComponentContainer implements ViewGroup {

    private Image leftView;
    private List<Image> rightViews;
    private DirectionalLayout rightMenuLayout;
    private boolean hasLeftIcon = false;


    public ActionBar(Context context) {
        super(context);
        init(null);
    }

    public ActionBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(attrSet);
    }

    public ActionBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(attrSet);
    }

    private void init(AttrSet attrSet) {
        if (attrSet == null) {
            return;
        }
        final Element leftIcon = attrSet.getAttr("leftIcon").map(Attr::getElement).orElse(null);
        if (leftIcon != null) {
            setLeftView(leftIcon);
            hasLeftIcon = true;
        }

        final String title = attrSet.getAttr("title").map(Attr::getStringValue).orElse("");
        setTitle(title);

        final Element right01 = attrSet.getAttr("right01").map(Attr::getElement).orElse(null);
        final Element right02 = attrSet.getAttr("right02").map(Attr::getElement).orElse(null);
        final Element right03 = attrSet.getAttr("right03").map(Attr::getElement).orElse(null);
        addRightMenu(right01, right02, right03);
    }


    public void setLeftView(Element drawable) {
        if (drawable != null) {
            if (leftView == null) {
                leftView = new Image(getContext());
            }
            leftView.setImageElement(drawable);
            leftView.setClickedListener(component -> {
                if (listener != null) {
                    listener.click(ActionBarType.LEFT_ICON, -1);
                }
            });
            DirectionalLayout leftLayout = new DirectionalLayout(getContext());
            leftLayout.setOrientation(HORIZONTAL);
            leftLayout.setPaddingLeft(50);
            final LayoutParams lp = new LayoutParams(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
            lp.gravity = Gravity.CENTER;
            leftLayout.setAlignment(LayoutAlignment.VERTICAL_CENTER);
            leftLayout.setLayoutConfig(lp);
            leftLayout.addComponent(leftView);
            addSystemView(leftLayout);
        }
    }

    public void setTitle(String title) {
        if (!isEmpty(title)) {
            DirectionalLayout titleLayout = new DirectionalLayout(getContext());
            titleLayout.setOrientation(HORIZONTAL);
            if (hasLeftIcon) {
                titleLayout.setPaddingLeft(150);
            } else {
                titleLayout.setPaddingLeft(50);
            }
            final LayoutParams lp;
            lp = new LayoutParams(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
            lp.gravity = Gravity.CENTER;
            titleLayout.setAlignment(LayoutAlignment.VERTICAL_CENTER);//CENTER);
            titleLayout.setLayoutConfig(lp);

            final Context context = getContext();
            Text mTitleTextView = new Text(context);
            mTitleTextView.setPaddingBottom(6);
            mTitleTextView.setMaxTextLines(1);
            mTitleTextView.setTextSize(52);
            mTitleTextView.setTextColor(Color.WHITE);
            mTitleTextView.setText(title);
            titleLayout.addComponent(mTitleTextView);
            addSystemView(titleLayout);
        }
    }

    /**
     * addRightMenu
     *
     * @param drawable drawable
     */
    public void addRightMenu(Element... drawable) {
        if (rightViews == null) {
            rightViews = new ArrayList<>();
        }
        for (Element item : drawable) {
            if (item != null) {
                Image menuView = createMenuView();
                menuView.setImageElement(item);
                rightViews.add(menuView);
            }
        }
        if (rightViews.size() > 0) {
            ensureRightMenuView(rightViews);
            addSystemView(rightMenuLayout);
        }
    }

    private void ensureRightMenuView(List<Image> rightViews) {
        if (rightMenuLayout == null) {
            rightMenuLayout = new DirectionalLayout(getContext());
            rightMenuLayout.setOrientation(HORIZONTAL);
            rightMenuLayout.setPaddingRight(50);
            final LayoutParams lp = new LayoutParams(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
            lp.gravity = Gravity.RIGHT;
            rightMenuLayout.setAlignment(LayoutAlignment.RIGHT | LayoutAlignment.VERTICAL_CENTER);
            rightMenuLayout.setLayoutConfig(lp);
            for (int i = 0; i < rightViews.size(); i++) {
                Image image = rightViews.get(i);
                image.setLayoutConfig(generateDefaultLayoutParams());
                image.setMarginLeft(50);
                final int position = i;
                image.setClickedListener(component -> {
                    if (listener != null) {
                        listener.click(ActionBarType.RIGHT_ICON, position);
                    }
                });
                rightMenuLayout.addComponent(image);
            }
        }
    }

    private ActionBarListener listener;

    /**
     * addClickListener
     *
     * @param listener listener
     */
    public void addClickListener(ActionBarListener listener) {
        this.listener = listener;
    }

    /**
     * ActionBarListener
     */
    public interface ActionBarListener {
        void click(ActionBarType clickType, int position);
    }

    /**
     * ActionBarType
     */
    public enum ActionBarType {
        LEFT_ICON,
        RIGHT_ICON
    }

    /**
     * clearRightMenu
     */
    public void clearRightMenu() {
        if (rightViews != null) {
            for (Image item : rightViews) {
                removeComponent(item);
            }
            rightViews.clear();
        }
    }

    /**
     * isEmpty
     *
     * @param cs cs
     * @return boolean
     */
    public boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }


    private Image createMenuView() {
        return new Image(getContext());
    }

    private void addSystemView(Component v) {
        final LayoutConfig vlp = v.getLayoutConfig();
        final LayoutParams lp;
        if (vlp == null) {
            lp = generateDefaultLayoutParams();
        } else if (!checkLayoutParams(vlp)) {
            lp = new LayoutParams(vlp);
        } else {
            lp = (LayoutParams) vlp;
        }
        addComponent(v, lp);
    }

    /**
     * checkLayoutParams
     *
     * @param p p
     * @return boolean
     */
    protected boolean checkLayoutParams(LayoutConfig p) {
        return p instanceof Toolbar.LayoutParams;
    }

    /**
     * generateDefaultLayoutParams
     *
     * @return LayoutParams
     */
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_CONTENT, LayoutParams.MATCH_CONTENT);
    }


    @Override
    public void requestDisallowInterceptTouchEvent(boolean b) {

    }

    @Override
    public boolean onInterceptTouchEvent(TouchEvent touchEvent) {
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(TouchEvent touchEvent) {
        return false;
    }

    @Override
    public boolean onTouchEvent(TouchEvent touchEvent) {
        return false;
    }

    @Override
    public boolean isConsumed() {
        return false;
    }

    /**
     * LayoutParams
     */
    public static class LayoutParams extends LayoutConfig {
        static final int CUSTOM = 0;
        static final int SYSTEM = 1;
        static final int EXPANDED = 2;
        public int gravity;
        int mViewType;

        public LayoutParams(Context var1, AttrSet var2) {
            super(var1, var2);
            this.mViewType = 0;
            this.gravity = 0;
            this.gravity = (Integer) var2.getAttr("layout_gravity").map(Attr::getIntegerValue).orElse(0);
        }

        public LayoutParams(int var1, int var2) {
            super(var1, var2);
            this.mViewType = 0;
            this.gravity = 0;
            this.gravity = 8388627;
        }

        public LayoutParams(int var1, int var2, int var3) {
            super(var1, var2);
            this.mViewType = 0;
            this.gravity = 0;
            this.gravity = var3;
        }

        public LayoutParams(int var1) {
            this(-2, -1, var1);
        }

        public LayoutParams(LayoutParams var1) {
            super(var1);
            this.mViewType = 0;
            this.gravity = 0;
            this.mViewType = var1.mViewType;
        }

        public LayoutParams(LayoutConfig var1) {
            super(var1);
            this.mViewType = 0;
            this.gravity = 0;
        }
    }

    public static class Gravity {
        /**
         * Constant indicating that no gravity has been set
         **/
        public static final int NO_GRAVITY = 0x0000;

        /**
         * Raw bit indicating the gravity for an axis has been specified.
         */
        public static final int AXIS_SPECIFIED = 0x0001;

        /**
         * Raw bit controlling how the left/top edge is placed.
         */
        public static final int AXIS_PULL_BEFORE = 0x0002;
        /**
         * Raw bit controlling how the right/bottom edge is placed.
         */
        public static final int AXIS_PULL_AFTER = 0x0004;
        /**
         * Raw bit controlling whether the right/bottom edge is clipped to its
         * container, based on the gravity direction being applied.
         */
        public static final int AXIS_CLIP = 0x0008;

        /**
         * Bits defining the horizontal axis.
         */
        public static final int AXIS_X_SHIFT = 0;
        /**
         * Bits defining the vertical axis.
         */
        public static final int AXIS_Y_SHIFT = 4;

        /**
         * Push object to the top of its container, not changing its size.
         */
        public static final int TOP = (AXIS_PULL_BEFORE | AXIS_SPECIFIED) << AXIS_Y_SHIFT;
        /**
         * Push object to the bottom of its container, not changing its size.
         */
        public static final int BOTTOM = (AXIS_PULL_AFTER | AXIS_SPECIFIED) << AXIS_Y_SHIFT;
        /**
         * Push object to the left of its container, not changing its size.
         */
        public static final int LEFT = (AXIS_PULL_BEFORE | AXIS_SPECIFIED) << AXIS_X_SHIFT;
        /**
         * Push object to the right of its container, not changing its size.
         */
        public static final int RIGHT = (AXIS_PULL_AFTER | AXIS_SPECIFIED) << AXIS_X_SHIFT;

        /**
         * Place object in the vertical center of its container, not changing its
         * size.
         */
        public static final int CENTER_VERTICAL = AXIS_SPECIFIED << AXIS_Y_SHIFT;
        /**
         * Grow the vertical size of the object if needed so it completely fills
         * its container.
         */
        public static final int FILL_VERTICAL = TOP | BOTTOM;

        /**
         * Place object in the horizontal center of its container, not changing its
         * size.
         */
        public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED << AXIS_X_SHIFT;
        /**
         * Grow the horizontal size of the object if needed so it completely fills
         * its container.
         */
        public static final int FILL_HORIZONTAL = LEFT | RIGHT;

        /**
         * Place the object in the center of its container in both the vertical
         * and horizontal axis, not changing its size.
         */
        public static final int CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL;

        /**
         * Grow the horizontal and vertical size of the object if needed so it
         * completely fills its container.
         */
        public static final int FILL = FILL_VERTICAL | FILL_HORIZONTAL;

        /**
         * Flag to clip the edges of the object to its container along the
         * vertical axis.
         */
        public static final int CLIP_VERTICAL = AXIS_CLIP << AXIS_Y_SHIFT;

        /**
         * Flag to clip the edges of the object to its container along the
         * horizontal axis.
         */
        public static final int CLIP_HORIZONTAL = AXIS_CLIP << AXIS_X_SHIFT;

        /**
         * Raw bit controlling whether the layout direction is relative or not (START/END instead of
         * absolute LEFT/RIGHT).
         */
        public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;

        /**
         * Binary mask to get the absolute horizontal gravity of a gravity.
         */
        public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED
                | AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
        /**
         * Binary mask to get the vertical gravity of a gravity.
         */
        public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED
                | AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;

        /**
         * Special constant to enable clipping to an overall display along the
         * vertical dimension.  This is not applied by default by
         * {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
         * yourself by calling {@link #applyDisplay}.
         */
        public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;

        /**
         * Special constant to enable clipping to an overall display along the
         * horizontal dimension.  This is not applied by default by
         * {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
         * yourself by calling {@link #applyDisplay}.
         */
        public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;

        /**
         * Push object to x-axis position at the start of its container, not changing its size.
         */
        public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;

        /**
         * Push object to x-axis position at the end of its container, not changing its size.
         */
        public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;

        /**
         * Binary mask for the horizontal gravity and script specific direction bit.
         */
        public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;

        /**
         * Apply a gravity constant to an object. This supposes that the layout direction is LTR.
         *
         * @param gravity   The desired placement of the object, as defined by the
         *                  constants in this class.
         * @param w         The horizontal size of the object.
         * @param h         The vertical size of the object.
         * @param container The frame of the containing space, in which the object
         *                  will be placed.  Should be large enough to contain the
         *                  width and height of the object.
         * @param outRect   Receives the computed frame of the object in its
         *                  container.
         */
        public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
            apply(gravity, w, h, container, 0, 0, outRect);
        }

        /**
         * Apply a gravity constant to an object and take care if layout direction is RTL or not.
         *
         * @param gravity         The desired placement of the object, as defined by the
         *                        constants in this class.
         * @param w               The horizontal size of the object.
         * @param h               The vertical size of the object.
         * @param container       The frame of the containing space, in which the object
         *                        will be placed.  Should be large enough to contain the
         *                        width and height of the object.
         * @param outRect         Receives the computed frame of the object in its
         *                        container.
         * @param layoutDirection The layout direction.
         */
        public static void apply(int gravity, int w, int h, Rect container,
                                 Rect outRect, Component.LayoutDirection layoutDirection) {
            int absGravity = getAbsoluteGravity(gravity, layoutDirection);
            apply(absGravity, w, h, container, 0, 0, outRect);
        }

        /**
         * Apply a gravity constant to an object.
         *
         * @param gravity   The desired placement of the object, as defined by the
         *                  constants in this class.
         * @param w         The horizontal size of the object.
         * @param h         The vertical size of the object.
         * @param container The frame of the containing space, in which the object
         *                  will be placed.  Should be large enough to contain the
         *                  width and height of the object.
         * @param xAdj      Offset to apply to the X axis.  If gravity is LEFT this
         *                  pushes it to the right; if gravity is RIGHT it pushes it to
         *                  the left; if gravity is CENTER_HORIZONTAL it pushes it to the
         *                  right or left; otherwise it is ignored.
         * @param yAdj      Offset to apply to the Y axis.  If gravity is TOP this pushes
         *                  it down; if gravity is BOTTOM it pushes it up; if gravity is
         *                  CENTER_VERTICAL it pushes it down or up; otherwise it is
         *                  ignored.
         * @param outRect   Receives the computed frame of the object in its
         *                  container.
         */
        public static void apply(int gravity, int w, int h, Rect container,
                                 int xAdj, int yAdj, Rect outRect) {
            switchO(gravity, w, h, container, xAdj, yAdj, outRect);
            switchT(gravity, w, h, container, xAdj, yAdj, outRect);
        }

        private static void switchT(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect) {
            switch (gravity & ((AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT)) {
                case 0:
                    outRect.top = container.top
                            + ((container.bottom - container.top - h) / 2) + yAdj;
                    outRect.bottom = outRect.top + h;
                    if ((gravity & (AXIS_CLIP << AXIS_Y_SHIFT))
                            == (AXIS_CLIP << AXIS_Y_SHIFT)) {
                        if (outRect.top < container.top) {
                            outRect.top = container.top;
                        }
                        if (outRect.bottom > container.bottom) {
                            outRect.bottom = container.bottom;
                        }
                    }
                    break;
                case AXIS_PULL_BEFORE << AXIS_Y_SHIFT:
                    outRect.top = container.top + yAdj;
                    outRect.bottom = outRect.top + h;
                    if ((gravity & (AXIS_CLIP << AXIS_Y_SHIFT))
                            == (AXIS_CLIP << AXIS_Y_SHIFT)) {
                        if (outRect.bottom > container.bottom) {
                            outRect.bottom = container.bottom;
                        }
                    }
                    break;
                case AXIS_PULL_AFTER << AXIS_Y_SHIFT:
                    outRect.bottom = container.bottom - yAdj;
                    outRect.top = outRect.bottom - h;
                    if ((gravity & (AXIS_CLIP << AXIS_Y_SHIFT))
                            == (AXIS_CLIP << AXIS_Y_SHIFT)) {
                        if (outRect.top < container.top) {
                            outRect.top = container.top;
                        }
                    }
                    break;
                default:
                    outRect.top = container.top + yAdj;
                    outRect.bottom = container.bottom + yAdj;
                    break;
            }
        }

        private static void switchO(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect) {
            switch (gravity & ((AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT)) {
                case 0:
                    outRect.left = container.left
                            + ((container.right - container.left - w) / 2) + xAdj;
                    outRect.right = outRect.left + w;
                    if ((gravity & (AXIS_CLIP << AXIS_X_SHIFT))
                            == (AXIS_CLIP << AXIS_X_SHIFT)) {
                        if (outRect.left < container.left) {
                            outRect.left = container.left;
                        }
                        if (outRect.right > container.right) {
                            outRect.right = container.right;
                        }
                    }
                    break;
                case AXIS_PULL_BEFORE << AXIS_X_SHIFT:
                    outRect.left = container.left + xAdj;
                    outRect.right = outRect.left + w;
                    if ((gravity & (AXIS_CLIP << AXIS_X_SHIFT))
                            == (AXIS_CLIP << AXIS_X_SHIFT)) {
                        if (outRect.right > container.right) {
                            outRect.right = container.right;
                        }
                    }
                    break;
                case AXIS_PULL_AFTER << AXIS_X_SHIFT:
                    outRect.right = container.right - xAdj;
                    outRect.left = outRect.right - w;
                    if ((gravity & (AXIS_CLIP << AXIS_X_SHIFT))
                            == (AXIS_CLIP << AXIS_X_SHIFT)) {
                        if (outRect.left < container.left) {
                            outRect.left = container.left;
                        }
                    }
                    break;
                default:
                    outRect.left = container.left + xAdj;
                    outRect.right = container.right + xAdj;
                    break;
            }
        }

        /**
         * Apply a gravity constant to an object.
         *
         * @param gravity         The desired placement of the object, as defined by the
         *                        constants in this class.
         * @param w               The horizontal size of the object.
         * @param h               The vertical size of the object.
         * @param container       The frame of the containing space, in which the object
         *                        will be placed.  Should be large enough to contain the
         *                        width and height of the object.
         * @param xAdj            Offset to apply to the X axis.  If gravity is LEFT this
         *                        pushes it to the right; if gravity is RIGHT it pushes it to
         *                        the left; if gravity is CENTER_HORIZONTAL it pushes it to the
         *                        right or left; otherwise it is ignored.
         * @param yAdj            Offset to apply to the Y axis.  If gravity is TOP this pushes
         *                        it down; if gravity is BOTTOM it pushes it up; if gravity is
         *                        CENTER_VERTICAL it pushes it down or up; otherwise it is
         *                        ignored.
         * @param outRect         Receives the computed frame of the object in its
         *                        container.
         * @param layoutDirection The layout direction.
         */
        public static void apply(int gravity, int w, int h, Rect container,
                                 int xAdj, int yAdj, Rect outRect, Component.LayoutDirection layoutDirection) {
            int absGravity = getAbsoluteGravity(gravity, layoutDirection);
            apply(absGravity, w, h, container, xAdj, yAdj, outRect);
        }

        /**
         * Apply additional gravity behavior based on the overall "display" that an
         * object exists in.  This can be used after
         * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
         * within a visible display.  By default this moves or clips the object
         * to be visible in the display; the gravity flags
         * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
         * can be used to change this behavior.
         *
         * @param gravity  Gravity constants to modify the placement within the
         *                 display.
         * @param display  The rectangle of the display in which the object is
         *                 being placed.
         * @param inoutObj Supplies the current object position; returns with it
         *                 modified if needed to fit in the display.
         */
        public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
            if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
                if (inoutObj.top < display.top) {
                    inoutObj.top = display.top;
                }
                if (inoutObj.bottom > display.bottom) {
                    inoutObj.bottom = display.bottom;
                }
            } else {
                int off = 0;
                if (inoutObj.top < display.top) {
                    off = display.top - inoutObj.top;
                } else if (inoutObj.bottom > display.bottom) {
                    off = display.bottom - inoutObj.bottom;
                }
                if (off != 0) {
                    if (inoutObj.bottom - inoutObj.top > (display.bottom - display.top)) {
                        inoutObj.top = display.top;
                        inoutObj.bottom = display.bottom;
                    } else {
                        inoutObj.top += off;
                        inoutObj.bottom += off;
                    }
                }
            }

            if ((gravity & DISPLAY_CLIP_HORIZONTAL) != 0) {
                if (inoutObj.left < display.left) {
                    inoutObj.left = display.left;
                }
                if (inoutObj.right > display.right) {
                    inoutObj.right = display.right;
                }
            } else {
                int off = 0;
                if (inoutObj.left < display.left) {
                    off = display.left - inoutObj.left;
                } else if (inoutObj.right > display.right) {
                    off = display.right - inoutObj.right;
                }
                if (off != 0) {
                    if (inoutObj.right - inoutObj.left > (display.right - display.left)) {
                        inoutObj.left = display.left;
                        inoutObj.right = display.right;
                    } else {
                        inoutObj.left += off;
                        inoutObj.right += off;
                    }
                }
            }
        }

        /**
         * Apply additional gravity behavior based on the overall "display" that an
         * object exists in.  This can be used after
         * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
         * within a visible display.  By default this moves or clips the object
         * to be visible in the display; the gravity flags
         * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
         * can be used to change this behavior.
         *
         * @param gravity         Gravity constants to modify the placement within the
         *                        display.
         * @param display         The rectangle of the display in which the object is
         *                        being placed.
         * @param inoutObj        Supplies the current object position; returns with it
         *                        modified if needed to fit in the display.
         * @param layoutDirection The layout direction.
         */
        public static void applyDisplay(int gravity, Rect display, Rect inoutObj, Component.LayoutDirection layoutDirection) {
            int absGravity = getAbsoluteGravity(gravity, layoutDirection);
            applyDisplay(absGravity, display, inoutObj);
        }

        /**
         * <p>Indicate whether the supplied gravity has a vertical pull.</p>
         *
         * @param gravity the gravity to check for vertical pull
         * @return true if the supplied gravity has a vertical pull
         */
        public static boolean isVertical(int gravity) {
            return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
        }

        /**
         * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
         *
         * @param gravity the gravity to check for horizontal pull
         * @return true if the supplied gravity has an horizontal pull
         */
        public static boolean isHorizontal(int gravity) {
            return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
        }

        /**
         * <p>Convert script specific gravity to absolute horizontal value.</p>
         * <p>
         * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
         * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
         *
         * @param gravity         The gravity to convert to absolute (horizontal) values.
         * @param layoutDirection The layout direction.
         * @return gravity converted to absolute (horizontal) values.
         */
        public static int getAbsoluteGravity(int gravity, Component.LayoutDirection layoutDirection) {
            int result = gravity;
            int type = result & RELATIVE_LAYOUT_DIRECTION;
            // If layout is script specific and gravity is horizontal relative (START or END)
            if (type > 0) {
                if ((result & com.ryan.ohos.extension.Gravity.START) == com.ryan.ohos.extension.Gravity.START) {
                    // Remove the START bit
                    result &= ~START;
                    if (layoutDirection == Component.LayoutDirection.RTL) {
                        // Set the RIGHT bit
                        result |= RIGHT;
                    } else {
                        // Set the LEFT bit
                        result |= LEFT;
                    }
                } else if ((result & com.ryan.ohos.extension.Gravity.END) == com.ryan.ohos.extension.Gravity.END) {
                    // Remove the END bit
                    result &= ~END;
                    if (layoutDirection == Component.LayoutDirection.RTL) {
                        // Set the LEFT bit
                        result |= LEFT;
                    } else {
                        // Set the RIGHT bit
                        result |= RIGHT;
                    }
                }
                // Don't need the script specific bit any more, so remove it as we are converting to
                // absolute values (LEFT or RIGHT)
                result &= ~RELATIVE_LAYOUT_DIRECTION;
            }
            return result;
        }

        public static boolean isEmpty(CharSequence cs) {
            return cs == null || cs.length() == 0;
        }

        public static int convert(String gravity) {
            if (isEmpty(gravity)) {
                return NO_GRAVITY;
            }

            String[] gravities = gravity.split("\\|");
            int result = -1;
            for (String s : gravities) {
                int i = convertString(s);
                if (result < 0) {
                    result = i;
                } else {
                    result |= i;
                }
            }

            return result;
        }

        public static int convertString(String g) {
            switch (g) {
                case "top":
                    return TOP;

                case "bottom":
                    return BOTTOM;

                case "left":
                    return LEFT;

                case "right":
                    return RIGHT;

                case "center_vertical":
                    return CENTER_VERTICAL;

                case "fill_vertical":
                    return FILL_VERTICAL;

                case "center_horizontal":
                    return CENTER_HORIZONTAL;

                case "fill_horizontal":
                    return FILL_HORIZONTAL;

                case "center":
                    return CENTER;

                case "fill":
                    return FILL;

                case "clip_vertical":
                    return CLIP_VERTICAL;

                case "clip_horizontal":
                    return CLIP_HORIZONTAL;
            }

            return NO_GRAVITY;
        }
    }

}
